#include "../../include/newpool/threadpool.h"

#include <vector>
#include <queue>
#include <memory>
#include <atomic>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
const int TASK_MAX_SIZE = 1024;
const int THREAD_MAX_SIZE = 10;
const int THREAD_MAX_IDLE_SECONDS = 60;
ThreadPool::ThreadPool() : initThreadSize_(4), taskSize_(0), maxTaskSize_(TASK_MAX_SIZE), mode_(PoolMode::FIXED), isPoolRunning_(false), idleThreadSize_(0), maxThreadSize_(THREAD_MAX_SIZE), currentThreadSize_(0)
{
}

ThreadPool::~ThreadPool()
{
    isPoolRunning_ = false;
    std::cout << "~ThreadPool()" << std::endl;
    std::unique_lock<std::mutex> lock(taskQueLock_);
    notEmpty_.notify_all();
    exitCon_.wait(lock, [&]() -> bool
                  { return threads_.size() == 0; });
    std::cout << "~ThreadPool1()" << std::endl;
}

void ThreadPool::start(int intThreadSize)
{
    initThreadSize_ = intThreadSize;
    currentThreadSize_ = intThreadSize;
    isPoolRunning_ = true;
    for (int i = 0; i < initThreadSize_; i++)
    {
        auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
        int threadId = ptr->getThreadId();
        threads_.emplace(threadId, std::move(ptr));
    };
    for (int i = 0; i < initThreadSize_; i++)
    {
        threads_[i]->start();
        idleThreadSize_++;
    }
};

bool ThreadPool::checkRuningState()
{
    return isPoolRunning_;
}

void ThreadPool::setMode(PoolMode mode)
{
    if (checkRuningState())
    {
        return;
    }
    mode_ = mode;
};

void ThreadPool::setMaxTaskSize(int maxTaskSize)
{
    if (checkRuningState())
    {
        return;
    }
    maxTaskSize_ = maxTaskSize;
};

void ThreadPool::threadFunc(int threadId)
{
    auto lastTime = std::chrono::high_resolution_clock().now();
    for (;;)
    {
        Task task;
        {
            std::unique_lock<std::mutex> lock(taskQueLock_);
            while (taskQueue_.size() == 0)
            {
                if (!isPoolRunning_)
                {
                    threads_.erase(threadId);
                    std::cout << "threadid:" << threadId << "exit" << std::endl;
                    exitCon_.notify_all();
                    return;
                }
                if (mode_ == PoolMode::CACHED)
                {
                    if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
                    {
                        auto now = std::chrono::high_resolution_clock().now();
                        auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
                        if (dur.count() > THREAD_MAX_IDLE_SECONDS && currentThreadSize_ > idleThreadSize_)
                        {
                            threads_.erase(threadId);
                            currentThreadSize_--;
                            idleThreadSize_--;
                            std::cout << "threadid:" << std::this_thread::get_id() << "exit" << std::endl;
                            return;
                        }
                    }
                }
                else
                {
                    notEmpty_.wait(lock);
                }
            }
            idleThreadSize_--;
            task = taskQueue_.front();
            taskQueue_.pop();
            taskSize_--;
            if (taskQueue_.size() > 0)
            {
                notEmpty_.notify_all();
            }
            notFull_.notify_all();
        }
        if (task != nullptr)
        {
            task();
        }
        idleThreadSize_++;
        lastTime = std::chrono::high_resolution_clock().now();
    }
};
